package nes;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * This item represents a circular buffer with fixed size. Linked list is used internally for data
 * storage.
 * 
 * @author Sergey Negrasov
 * 
 * @param <E> Storage class type.
 */
public class CircularBuffer<E> implements Collection<E> {

  /**
   * Buffer used for item storage.
   */
  private List<E> buffer;

  /**
   * Maximum capacity of this circular buffer.
   */
  private int capacity;

  /**
   * Simple constructor.
   * 
   * @param capacity maximum capacity of the buffer.
   */
  public CircularBuffer(int capacity) {
    buffer = new LinkedList<E>();
    this.capacity = capacity;
  }

  /**
   * Check for emptiness.
   * 
   * @return {@code true} if the buffer is empty, otherwise {code false}.
   */
  @Override
  public boolean isEmpty() {
    return buffer.isEmpty();
  }

  /**
   * Returns the current size of the buffer.
   * 
   * @return size of the buffer.
   */
  @Override
  public int size() {
    return buffer.size();
  }

  /**
   * Adds an item to the circular buffer.
   * 
   * @param item item to be added.
   * @return {@code true}.
   */
  @Override
  public boolean add(E item) {
    if (buffer.size() == capacity) {
      buffer.remove(0);
    }
    buffer.add(item);
    return true;
  }

  /**
   * Adds a collection of items to the buffer.
   * 
   * @param items collection of items to add to the circular buffer.
   * @return {@code true}
   */
  @Override
  public boolean addAll(Collection<? extends E> items) {
    for (E nextitem : items) {
      this.add(nextitem);
    }
    return true;
  }

  /**
   * Clears the circular buffer.
   */
  @Override
  public void clear() {
    buffer.clear();
  }

  /**
   * Checks for existence of an object in the buffer.
   * 
   * @param o object to check.
   * @return {@code true} on success, otherwise {@code false}.
   */
  @Override
  public boolean contains(Object o) {
    return buffer.contains(o);
  }

  /**
   * Checks for existence of a collection of object in the buffer.
   * 
   * @param items objects to check.
   * @return {@code true} on success, otherwise {@code false}.
   */
  @Override
  public boolean containsAll(Collection<?> items) {
    return buffer.containsAll(items);
  }

  /**
   * Removes an object in the buffer.
   * 
   * @param item object to remove.
   * @return {@code true} on success, otherwise {@code false}.
   */
  @Override
  public boolean remove(Object item) {
    return buffer.remove(item);
  }

  /**
   * Removes a collection object in the buffer.
   * 
   * @param items object to remove.
   * @return {@code true} on success, otherwise {@code false}.
   */
  @Override
  public boolean removeAll(Collection<?> items) {
    throw new UnsupportedOperationException();
  }

  /**
   * Unused for the circular buffer.
   * 
   * @param items object to retain.
   * @return {@code true} on success, otherwise {@code false}.
   */
  @Override
  public boolean retainAll(Collection<?> items) {
    throw new UnsupportedOperationException();
  }

  /**
   * Convert the circular buffer to an array.
   * 
   * @return an array of circular buffer elements.
   */
  @Override
  public Object[] toArray() {
    return buffer.toArray();
  }

  /**
   * Returns an array of all the elements in an Array. The runtime type of the returned array is
   * that of the specified array.
   * 
   * @param item items to retain.
   * @param <T> return type.
   * @return array of elements in both buffer and item.
   */
  @Override
  public <T> T[] toArray(T[] item) {
    return buffer.toArray(item);
  }

  /**
   * Returns a circular buffer iterator.
   * 
   * @return iterator.
   */
  @Override
  public Iterator<E> iterator() {
    return buffer.iterator();
  }

  /**
   * Returns the the textual representation of the circular buffer.
   * 
   * @return a string representation of the buffer.
   */
  @Override
  public String toString() {
    return buffer.toString();
  }

  /**
   * Returns the top element to the buffer.
   * 
   * @return top element.
   */
  public E getTop() {
    if (this.isEmpty()) {
      return null;
    }
    return buffer.get(buffer.size() - 1);
  }

  /**
   * Returns the content as  a list.
   * 
   * @return list of elements implemented as array list.
   */
  public List<E> toList() {
    return new ArrayList<E>(buffer);
  }

}
